package com.youlu.campus.web.data.service.impl;

import com.mongodb.client.result.UpdateResult;
import com.youlu.campus.base.common.Constants;

import com.youlu.campus.service.base.BaseService;
import com.youlu.campus.web.data.entity.DayConversionStatistics;
import com.youlu.campus.web.data.service.DayConversionStatisticsService;
import com.youlu.campus.web.common.utils.DateTimeUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class DayConversionStatisticsServiceImpl extends BaseService implements DayConversionStatisticsService {
    /**
     * 24小时过期
     */
    private final Long keyExpire = 24 * 60 * 60L;

    @Override
    public void accClockInAuthorizeUsers(String tenantId, String clockInId, String userId) {
        log.info(":>>> 开始打卡转化->授权用户数,租户ID:{},打卡ID:{},用户:{}", tenantId, clockInId, userId);
        if (StringUtils.isBlank(tenantId) || StringUtils.isBlank(clockInId) || StringUtils.isBlank(userId)) {
            log.error(":>>> 打卡转化->授权用户数参数错误");
            return;
        }
        String key = this.getAccClockInAuthorizeUsersRedisKey(tenantId);
        try {
            this.updateCache(key);
        } catch (Exception e) {
            log.error(":>>> 设置租户:{} 打卡转化->授权用户数用户数错误:{}", e);
        } finally {
            this.updateAccClockInAuthorizeUsers(tenantId, clockInId, userId);
        }
    }

    @Override
    public void accClockInJoinGroupUsers(String tenantId, String clockInId, String userId) {
        log.info(":>>> 开始打卡转化->创建/加入小组用户数,租户ID:{},打卡ID:{},用户:{}", tenantId, clockInId, userId);
        if (StringUtils.isBlank(tenantId) || StringUtils.isBlank(clockInId) || StringUtils.isBlank(userId)) {
            log.error(":>>> 打卡转化->->创建/加入小组用户数参数错误");
            return;
        }
        String key = this.getAccClockInJoinGroupUsersRedisKey(tenantId);
        try {
            this.updateCache(key);
        } catch (Exception e) {
            log.error(":>>> 设置租户:{} 打卡转化->->创建/加入小组用户数错误:{}", e);
        } finally {
            this.updateAccClockInJoinGroupUsers(tenantId, clockInId, userId);
        }
    }

    @Override
    public void accClockInFirstUsers(String tenantId, String clockInId, String userId) {
        log.info(":>>> 开始打卡转化->首次打卡用户数,租户ID:{},打卡ID:{},用户:{}", tenantId, clockInId, userId);
        if (StringUtils.isBlank(tenantId) || StringUtils.isBlank(clockInId) || StringUtils.isBlank(userId)) {
            log.error(":>>> 打卡转化->->首次打卡用户数参数错误");
            return;
        }
        String key = this.getAccClockInFirstUsersRedisKey(tenantId);
        try {
            this.updateCache(key);
        } catch (Exception e) {
            log.error(":>>> 设置租户:{} 打卡转化->->首次打卡用户数错误:{}", e);
        } finally {
            this.updateAccClockInFirstUsers(tenantId, clockInId, userId);
        }
    }

    @Override
    public void accAppealCertificate(String tenantId, String certificateId, String userId) {
        log.info(":>>> 开始打卡转化->申请证书用户数,租户ID:{},证书ID:{},用户:{}", tenantId, certificateId, userId);
        if (StringUtils.isBlank(tenantId) || StringUtils.isBlank(certificateId) || StringUtils.isBlank(userId)) {
            log.error(":>>> 打卡转化->->申请证书用户数参数错误");
            return;
        }
        String key = this.getAccAppealCertificateRedisKey(tenantId);
        try {
            this.updateCache(key);
        } catch (Exception e) {
            log.error(":>>> 设置租户:{} 打卡转化->->申请证书用户数错误:{}", e);
        } finally {
            this.updateAccAppealCertificate(tenantId, certificateId, userId);
        }

    }

    @Override
    public void accPaymentCertificate(String tenantId, String certificateId, String orderId, String userId) {
        log.info(":>>> 开始打卡转化->付费下单用户数,租户ID:{},证书ID:{},用户:{},订单ID:{}", tenantId, certificateId, userId, orderId);
        if (StringUtils.isBlank(tenantId) || StringUtils.isBlank(certificateId) || StringUtils.isBlank(userId)) {
            log.error(":>>> 打卡转化->->付费下单用户数参数错误");
            return;
        }
        String key = this.getAccPaymentCertificateRedisKey(tenantId);
        try {
            this.updateCache(key);
        } catch (Exception e) {
            log.error(":>>> 设置租户:{} 打卡转化->->付费下单用户数用户数错误:{}", e);
        } finally {
            this.updateAccPaymentCertificate(tenantId, certificateId, orderId, userId);
        }
    }

    @Override
    public void accTeamLeaderAuthorizeUsers(String tenantId, String groupId, String userId) {
        log.info(":>>> 开始组长转化->授权用户数,租户ID:{},组ID:{},用户:{}", tenantId, groupId, userId);
        if (StringUtils.isBlank(tenantId) || StringUtils.isBlank(groupId) || StringUtils.isBlank(userId)) {
            log.error(":>>> 开始组长转化->授权用户数参数错误");
            return;
        }
        String key = this.getAccTeamLeaderAuthorizeUsersRedisKey(tenantId);
        try {
            this.updateCache(key);
        } catch (Exception e) {
            log.error(":>>> 设置租户:{} 开始组长转化->授权用户数错误:{}", e);
        } finally {
            this.updateAccTeamLeaderAuthorizeUsers(tenantId, groupId, userId);
        }
    }

    @Override
    public void accCreateGroupUsers(String tenantId, String groupId, String userId) {
        log.info(":>>> 开始组长转化->创建小组用户数,租户ID:{},组ID:{},用户:{}", tenantId, groupId, userId);
        if (StringUtils.isBlank(tenantId) || StringUtils.isBlank(groupId) || StringUtils.isBlank(userId)) {
            log.error(":>>> 开始组长转化->创建小组用户数参数错误");
            return;
        }
        String key = this.getAccCreateGroupUsersRedisKey(tenantId);
        try {
            this.updateCache(key);
        } catch (Exception e) {
            log.error(":>>> 设置租户:{} 开始组长转化->创建小组用户数错误:{}", e);
        } finally {
            this.updateAccCreateGroupUsers(tenantId, groupId, userId);
        }
    }

    @Override
    public void accAccomplishTaskUsers(String tenantId, String groupId, String userId) {
        log.info(":>>> 开始组长转化->完成组建任务用户数,租户ID:{},组ID:{},用户:{}", tenantId, groupId, userId);
        if (StringUtils.isBlank(tenantId) || StringUtils.isBlank(groupId) || StringUtils.isBlank(userId)) {
            log.error(":>>> 开始组长转化->完成组建任务用户数参数错误");
            return;
        }
        String key = this.getAccAccomplishTaskUsersRedisKey(tenantId);
        try {
            this.updateCache(key);
        } catch (Exception e) {
            log.error(":>>> 设置租户:{} 开始组长转化->完成组建任务用户数错误:{}", e);
        } finally {
            this.updateAccAccomplishTaskUsers(tenantId, groupId, userId);
        }
    }

    @Override
    public void accTeamMemberAuthorizeUsers(String tenantId, String groupId, String userId) {
        log.info(":>>> 开始组员转化->授权用户数,租户ID:{},组ID:{},用户:{}", tenantId, groupId, userId);
        if (StringUtils.isBlank(tenantId) || StringUtils.isBlank(groupId) || StringUtils.isBlank(userId)) {
            log.error(":>>> 开始组员转化->授权用户数参数错误");
            return;
        }
        String key = this.getAccTeamMemberAuthorizeUsersRedisKey(tenantId);
        try {
            this.updateCache(key);
        } catch (Exception e) {
            log.error(":>>> 设置租户:{} 开始组员转化->授权用户用户数错误:{}", e);
        } finally {
            this.updateAccTeamMemberAuthorizeUsers(tenantId, groupId, userId);
        }
    }

    @Override
    public void accTeamMemberJoinGroupUsers(String tenantId, String groupId, String userId) {
        log.info(":>>> 开始组员转化->加入组织用户数,租户ID:{},组ID:{},用户:{}", tenantId, groupId, userId);
        if (StringUtils.isBlank(tenantId) || StringUtils.isBlank(groupId) || StringUtils.isBlank(userId)) {
            log.error(":>>> 开始组员转化->加入组织用户数参数错误");
            return;
        }
        String key = this.getAccTeamMemberJoinGroupUsersRedisKey(tenantId);
        try {
            this.updateCache(key);
        } catch (Exception e) {
            log.error(":>>> 设置租户:{} 开始组员转化->加入组织用户用户数错误:{}", e);
        } finally {
            this.updateAccTeamMemberJoinGroupUsers(tenantId, groupId, userId);
        }
    }

    /**
     * 更新Redis Key缓存
     *
     * @param key
     */
    // TODO:RedisTemplate 序列化方式可能存在问题
    private void updateCache(String key) {
        Long count = redisTemplate.opsForValue().increment(key, 1L);
        log.info(":>>> 计数器:{},结果为:{}", key, count);
        if (count.compareTo(0L) == 1) {
            redisTemplate.expire(key, keyExpire, TimeUnit.SECONDS);
            log.debug(":>>> 设置key:{} 过期时间24小时之后 ", key);
        }
    }

    protected String getAccClockInAuthorizeUsersRedisKey(String tenantId) {
        String key = String.format(Constants.SYSTEM_CONVERSION_CLOCKIN_AUTHORIZER_PEFIX, tenantId, DateTimeUtils.getDay0Str());
        log.info(":>>> 租户:{} 打卡转化-授权用户数 Redis Key:{}", tenantId, key);
        return key;
    }

    protected String getAccClockInJoinGroupUsersRedisKey(String tenantId) {
        String key = String.format(Constants.SYSTEM_CONVERSION_CLOCKIN_JOIN_GROUP_PEFIX, tenantId, DateTimeUtils.getDay0Str());
        log.info(":>>> 租户:{} 打卡转化-打卡创建/加入小组用户数 Redis Key:{}", tenantId, key);
        return key;
    }

    protected String getAccClockInFirstUsersRedisKey(String tenantId) {
        String key = String.format(Constants.SYSTEM_CONVERSION_CLOCKIN_FIRST_PEFIX, tenantId, DateTimeUtils.getDay0Str());
        log.info(":>>> 租户:{} 打卡转化-首次打卡 Redis Key:{}", tenantId, key);
        return key;
    }

    protected String getAccAppealCertificateRedisKey(String tenantId) {
        String key = String.format(Constants.SYSTEM_CONVERSION_APPEAL_CERTIFICATION_PEFIX, tenantId, DateTimeUtils.getDay0Str());
        log.info(":>>> 租户:{} 打卡转化-申请证书 Redis Key:{}", tenantId, key);
        return key;
    }

    protected String getAccPaymentCertificateRedisKey(String tenantId) {
        String key = String.format(Constants.SYSTEM_CONVERSION_PAY_CERTIFICATION_PEFIX, tenantId, DateTimeUtils.getDay0Str());
        log.info(":>>> 租户:{} 打卡转化-付费下单证书 Redis Key:{}", tenantId, key);
        return key;
    }

    protected String getAccTeamLeaderAuthorizeUsersRedisKey(String tenantId) {
        String key = String.format(Constants.SYSTEM_CONVERSION_TEAM_LEADER_AUTHORIZER_PEFIX, tenantId, DateTimeUtils.getDay0Str());
        log.info(":>>> 租户:{} 组长转化-授权用户数 Redis Key:{}", tenantId, key);
        return key;
    }

    protected String getAccCreateGroupUsersRedisKey(String tenantId) {
        String key = String.format(Constants.SYSTEM_CONVERSION_TEAM_LEADER_CREATE_GROUP_PEFIX, tenantId, DateTimeUtils.getDay0Str());
        log.info(":>>> 租户:{} 组长转化-创建小组用户数 Redis Key:{}", tenantId, key);
        return key;
    }
    protected String getAccAccomplishTaskUsersRedisKey(String tenantId) {
        String key = String.format(Constants.SYSTEM_CONVERSION_TEAM_LEADER_COMPLETE_TASK_PEFIX, tenantId, DateTimeUtils.getDay0Str());
        log.info(":>>> 租户:{} 组长转化-完成组建任务用户数 Redis Key:{}", tenantId, key);
        return key;
    }
    protected String getAccTeamMemberAuthorizeUsersRedisKey(String tenantId) {
        String key = String.format(Constants.SYSTEM_CONVERSION_GROUP_MEMBER_AUTHORIZER_PEFIX, tenantId, DateTimeUtils.getDay0Str());
        log.info(":>>> 租户:{} 组员转化-授权用户数 Redis Key:{}", tenantId, key);
        return key;
    }
    protected String getAccTeamMemberJoinGroupUsersRedisKey(String tenantId) {
        String key = String.format(Constants.SYSTEM_CONVERSION_GROUP_MEMBER_JOIN_GROUP_PEFIX, tenantId, DateTimeUtils.getDay0Str());
        log.info(":>>> 租户:{} 组员转化-加入组织用户数 Redis Key:{}", tenantId, key);
        return key;
    }

    /**
     * 更新转化数据统计
     *
     * @param d
     * @param tenantId
     * @param startTime
     * @param field
     * @param incr
     */
        private void updateDayConversionStatistics(DayConversionStatistics d, String tenantId,
                                               Date startTime, String field, Integer incr) {
        if (Objects.isNull(d)) {
            d = new DayConversionStatistics();
            switch (field) {
                case "authorizeUsersForCheckIn":
                    d.setAuthorizeUsersForCheckIn(incr);
                    break;
                case "joinInGroupUsersForCheckIn":
                    d.setJoinInGroupUsersForCheckIn(incr);
                    break;
                case "firstCheckIn":
                    d.setFirstCheckIn(incr);
                    break;
                case "authorizeUsersForGroupMember":
                    d.setAuthorizeUsersForGroupMember(incr);
                    break;
                case "joinInGourpUsers":
                    d.setJoinInGourpUsers(incr);
                    break;
                case "authorizeForGroup":
                    d.setAuthorizeForGroup(incr);
                    break;
                case "createGroupUsersNum":
                    d.setCreateGroupUsersNum(incr);
                    break;
                case "completeTaskUsersNum":
                    d.setCompleteTaskUsersNum(incr);
                case "appealCertificateUsersNum":
                    d.setAppealCertificateUsersNum(incr);
                case "payCertificateUsersNum":
                    d.setPayCertificateUsersNum(incr);
                    break;
                default:
                    break;
            }
            d.setCreatedTime(new Date());
            d.setDeleted(Boolean.FALSE);
            d.setTenantId(tenantId);
            d.setStatisticsTime(startTime);
            mongoTemplate.insert(d);
            log.info(":>>> 租户:{} 转化数据:{} 统计不存在,新增", tenantId, startTime);
            return;
        }
        Query query1 = new Query();
        query1.addCriteria(Criteria.where("id").is(d.getId()));
        Update update = new Update();
        update.inc(field, incr);
        update.set("updatedTime", new Date());
        UpdateResult re = mongoTemplate.updateFirst(query1, update, DayConversionStatistics.class);
        log.info(":>>> 租户:{} 转化:{} 统计存在,修改结果:{}", tenantId, startTime, re.getModifiedCount() > 0 ? true : false);
    }

        private void updateAccClockInAuthorizeUsers(String tenantId, String clockInId, String userId) {
        Query query = new Query();
        Date startTime = DateTimeUtils.getDay0Date();
        Criteria criteria = new Criteria().andOperator(Criteria.where("statisticsTime").gte(DateTimeUtils.dateToISODate(startTime)), Criteria.where("statisticsTime").lte(DateTimeUtils.dateToISODate(new Date(startTime.getTime() + 24 * 60 * 60 * 1000L))));
        query.addCriteria(criteria);
        DayConversionStatistics d = mongoTemplate.findOne(query, DayConversionStatistics.class);
        this.updateDayConversionStatistics(d, tenantId,
                startTime, "authorizeUsersForCheckIn", 1);
    }

        private void updateAccClockInJoinGroupUsers(String tenantId, String clockInId, String userId) {
        Query query = new Query();
        Date startTime = DateTimeUtils.getDay0Date();
        Criteria criteria = new Criteria().andOperator(Criteria.where("statisticsTime").gte(DateTimeUtils.dateToISODate(startTime)), Criteria.where("statisticsTime").lte(DateTimeUtils.dateToISODate(new Date(startTime.getTime() + 24 * 60 * 60 * 1000L))));
        query.addCriteria(criteria);
        DayConversionStatistics d = mongoTemplate.findOne(query, DayConversionStatistics.class);
        this.updateDayConversionStatistics(d, tenantId,
                startTime, "joinInGroupUsersForCheckIn", 1);
    }

        private void updateAccClockInFirstUsers(String tenantId, String clockInId, String userId) {
        Query query = new Query();
        Date startTime = DateTimeUtils.getDay0Date();
        Criteria criteria = new Criteria().andOperator(Criteria.where("statisticsTime").gte(DateTimeUtils.dateToISODate(startTime)), Criteria.where("statisticsTime").lte(DateTimeUtils.dateToISODate(new Date(startTime.getTime() + 24 * 60 * 60 * 1000L))));
        query.addCriteria(criteria);
        DayConversionStatistics d = mongoTemplate.findOne(query, DayConversionStatistics.class);
        this.updateDayConversionStatistics(d, tenantId,
                startTime, "firstCheckIn", 1);
    }

        private void updateAccAppealCertificate(String tenantId, String certificationId, String userId) {
        Query query = new Query();
        Date startTime = DateTimeUtils.getDay0Date();
        Criteria criteria = new Criteria().andOperator(Criteria.where("statisticsTime").gte(DateTimeUtils.dateToISODate(startTime)), Criteria.where("statisticsTime").lte(DateTimeUtils.dateToISODate(new Date(startTime.getTime() + 24 * 60 * 60 * 1000L))));
        query.addCriteria(criteria);
        DayConversionStatistics d = mongoTemplate.findOne(query, DayConversionStatistics.class);
        this.updateDayConversionStatistics(d, tenantId,
                startTime, "appealCertificateUsersNum", 1);
    }

        private void updateAccPaymentCertificate(String tenantId, String certificationId, String orderId, String userId) {
        Query query = new Query();
        Date startTime = DateTimeUtils.getDay0Date();
        Criteria criteria = new Criteria().andOperator(Criteria.where("statisticsTime").gte(DateTimeUtils.dateToISODate(startTime)), Criteria.where("statisticsTime").lte(DateTimeUtils.dateToISODate(new Date(startTime.getTime() + 24 * 60 * 60 * 1000L))));
        query.addCriteria(criteria);
        DayConversionStatistics d = mongoTemplate.findOne(query, DayConversionStatistics.class);
        this.updateDayConversionStatistics(d, tenantId,
                startTime, "payCertificateUsersNum", 1);
    }

        private void updateAccTeamLeaderAuthorizeUsers(String tenantId, String groupId, String userId) {
        Query query = new Query();
        Date startTime = DateTimeUtils.getDay0Date();
        Criteria criteria = new Criteria().andOperator(Criteria.where("statisticsTime").gte(DateTimeUtils.dateToISODate(startTime)), Criteria.where("statisticsTime").lte(DateTimeUtils.dateToISODate(new Date(startTime.getTime() + 24 * 60 * 60 * 1000L))));
        query.addCriteria(criteria);
        DayConversionStatistics d = mongoTemplate.findOne(query, DayConversionStatistics.class);
        this.updateDayConversionStatistics(d, tenantId,
                startTime, "authorizeForGroup", 1);
    }

        private void updateAccCreateGroupUsers(String tenantId, String groupId, String userId) {
        Query query = new Query();
        Date startTime = DateTimeUtils.getDay0Date();
        Criteria criteria = new Criteria().andOperator(Criteria.where("statisticsTime").gte(DateTimeUtils.dateToISODate(startTime)), Criteria.where("statisticsTime").lte(DateTimeUtils.dateToISODate(new Date(startTime.getTime() + 24 * 60 * 60 * 1000L))));
        query.addCriteria(criteria);
        DayConversionStatistics d = mongoTemplate.findOne(query, DayConversionStatistics.class);
        this.updateDayConversionStatistics(d, tenantId,
                startTime, "createGroupUsersNum", 1);
    }
        private void updateAccAccomplishTaskUsers(String tenantId, String groupId, String userId) {
        Query query = new Query();
        Date startTime = DateTimeUtils.getDay0Date();
        Criteria criteria = new Criteria().andOperator(Criteria.where("statisticsTime").gte(DateTimeUtils.dateToISODate(startTime)), Criteria.where("statisticsTime").lte(DateTimeUtils.dateToISODate(new Date(startTime.getTime() + 24 * 60 * 60 * 1000L))));
        query.addCriteria(criteria);
        DayConversionStatistics d = mongoTemplate.findOne(query, DayConversionStatistics.class);
        this.updateDayConversionStatistics(d, tenantId,
                startTime, "completeTaskUsersNum", 1);
    }
        private void updateAccTeamMemberAuthorizeUsers(String tenantId, String groupId, String userId) {
        Query query = new Query();
        Date startTime = DateTimeUtils.getDay0Date();
        Criteria criteria = new Criteria().andOperator(Criteria.where("statisticsTime").gte(DateTimeUtils.dateToISODate(startTime)), Criteria.where("statisticsTime").lte(DateTimeUtils.dateToISODate(new Date(startTime.getTime() + 24 * 60 * 60 * 1000L))));
        query.addCriteria(criteria);
        DayConversionStatistics d = mongoTemplate.findOne(query, DayConversionStatistics.class);
        this.updateDayConversionStatistics(d, tenantId,
                startTime, "authorizeUsersForGroupMember", 1);
    }
        private void updateAccTeamMemberJoinGroupUsers(String tenantId, String groupId, String userId) {
        Query query = new Query();
        Date startTime = DateTimeUtils.getDay0Date();
        Criteria criteria = new Criteria().andOperator(Criteria.where("statisticsTime").gte(DateTimeUtils.dateToISODate(startTime)), Criteria.where("statisticsTime").lte(DateTimeUtils.dateToISODate(new Date(startTime.getTime() + 24 * 60 * 60 * 1000L))));
        query.addCriteria(criteria);
        DayConversionStatistics d = mongoTemplate.findOne(query, DayConversionStatistics.class);
        this.updateDayConversionStatistics(d, tenantId,
                startTime, "joinInGourpUsers", 1);
    }
}
